use std::{fmt, sync::Arc};

use crate::{native::StackTraceElement, rtda::Slots};

use super::heap::{Class, ArrayObject, Field, Method};

// use super::{Class, ArrayObject, field::Field};

#[derive(Clone)]
pub enum DataType {
    Slots(Slots), // 实例变量
    Array(ArrayObject)
}

#[derive(Clone)]
pub enum ExtraType {
    Class(Arc<Class>),
    StackTraceElements(Vec<StackTraceElement>),
    Field(Arc<Field>),
    Method(Arc<Method>),
}

impl ExtraType {
    pub fn get_method(&self) -> Arc<Method> {
        match self {
            Self::Method(method) => method.clone(),
            _ => panic!("ExtraType is not Method")
        }
    }

    pub fn get_class(&self) -> Arc<Class> {
        match self {
            Self::Class(class) => class.clone(),
            _ => panic!("ExtraType is not Class")
        }
    }
}

#[derive(Clone)]
pub struct Object {
    class: Arc<Class>,
    data: DataType,
    extra: Option<ExtraType>, // 记录Object结构体实例的额外信息
}

impl Object {
    pub fn new(class: Arc<Class>) -> Object {
        let slots = Slots::new_slots(class.get_instance_slot_count());
        Object {
            class,
            data: DataType::Slots(slots),
            extra: None,
        }
    }

    pub fn new_array(class: Arc<Class>, array: ArrayObject) -> Object {
        Object {
            class,
            data: DataType::Array(array),
            extra: None,
        }
    }

    pub fn new_object(class: Arc<Class>, data: DataType) -> Object {
        Object { class, data, extra: None }
    }

    pub fn get_fields_mut(&mut self) -> &mut Slots {
        match &mut self.data {
            DataType::Slots(slot) => slot,
            _ => panic!("type is not slot")
        }
    }

    pub fn array_length(&self) -> usize {
        match &self.data {
            DataType::Array(arr) => arr.array_length(),
            _ => panic!("type is not array")
        }
    }

    pub fn array(&mut self) -> &mut ArrayObject {
        match &mut self.data {
            DataType::Array(arr) => arr,
            _ => panic!("type is not array")
        }
    }

    pub fn get_array(&self) -> &ArrayObject {
        match &self.data {
            DataType::Array(arr) => arr,
            _ => panic!("type is not array")
        }
    }

    pub fn get_data(&self) -> &DataType {
        &self.data
    }

    pub fn get_data_mut(&mut self) -> &mut DataType {
        &mut self.data
    }

    pub fn set_extra(&mut self, extra: Option<ExtraType>) {
        self.extra = extra;
    }

    pub fn get_extra(&self) -> Option<ExtraType> {
        self.extra.clone()
    }

    pub fn is_instance_of(&self, class: Arc<Class>) -> bool {
        class.is_assignable_from(self.class.as_ref())
    }

    pub fn get_class(&self) -> Arc<Class> {
        self.class.clone()
    }

    pub fn set_ref_var(&mut self, name: &str, descriptor: &str, _ref: *mut Object) {
        let field = Class::get_field(Some(self.class.clone()), name, descriptor, false);

        if let DataType::Slots(slots) = &mut self.data {
            slots.set_ref(field.unwrap().get_slot_id(), Some(_ref));
        }
    }

    pub fn get_ref_var(&self, name: &str, descriptor: &str) -> Option<*mut Object> {
        let field = Class::get_field(Some(self.class.clone()), name, descriptor, false);
        
        if let Some(field) = field {
            if let DataType::Slots(slots) = &self.data {
                slots.get_ref(field.get_slot_id())
            } else {
                None
            }
        } else {
            None
        }
    }

    pub fn set_int_var(&mut self, name: &str, descriptor: &str, val: i32) {
        // let f = |field: &Rc<RefCell<Field>>| field.is_static() == false && field.get_name() == name && field.get_descriptor() == descriptor;
        let field = Class::get_field(Some(self.class.clone()), name, descriptor, false);

        if let DataType::Slots(slots) = &mut self.data {
            slots.set_int(field.unwrap().get_slot_id(), val);
        }
    }

    pub fn get_int_var(&self, name: &str, descriptor: &str) -> i32 {
        // let f = |field: &Rc<RefCell<Field>>| field.is_static() == false && field.get_name() == name && field.get_descriptor() == descriptor;
        let field = Class::get_field(Some(self.class.clone()), name, descriptor, false);

        // 默认为0
        if let DataType::Slots(slots) = &self.data {
            slots.get_int(field.unwrap().get_slot_id())
        } else {
            0
        }
    }

    pub fn get_boolean_var(&self, name: &str) -> bool {
        self.get_int_var(name, "Z") == 1
    }
}


impl fmt::Display for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let class_name = self.class.get_name();
        let data = match &self.data {
            DataType::Slots(slots) => format!("Slots: {:?}", slots),
            DataType::Array(array) => format!("Array: {}", array)
        };
        let extra = match &self.extra {
            Some(e) => {
                match e {
                    ExtraType::Class(class) => format!("Class: {}", class.get_name()),
                    ExtraType::Field(field) => format!("Field: {}{}", field.get_name(), field.get_descriptor()),
                    ExtraType::Method(method) => format!("Method: {}{}", method.get_name(), method.get_descriptor()),
                    ExtraType::StackTraceElements(_) => "StackTraceElements".to_string()
                }
            },
            None => "None".to_string()
        };
        let s = format!("Object:{{ \n  class: {},\n  data: {}\n  extra: {}\n }}", class_name, data, extra);
        write!(f, "{}", s)
    }
}